package com.yf.ability.demo.utils;

import com.baomidou.mybatisplus.annotation.TableName;
import com.yf.ability.demo.service.ProtectService;
import com.yf.base.api.annon.DataProtect;
import com.yf.base.api.api.dto.BaseIdsReqDTO;
import com.yf.base.api.exception.ServiceException;
import com.yf.base.utils.InjectUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 数据字典AOP类，处理数据字典值
 *
 * @author bool
 */
@Component
@Slf4j
public class ProtectUtils {

    /**
     * 数据标识，系统全部用id，所以不需要一一定义
     */
    private static final String DATA_ID = "id";


    @Autowired
    private HttpServletRequest request;

    @Autowired
    private ProtectService protectService;

    /**
     * 禁止操作接口
     */
    private static final List<String> FORBIDDEN_API = new ArrayList<>(Arrays.asList(
            "/api/sys/config/save",
            "/api/sys/config/prop/save",
            "/api/sys/menu/save",
            "/api/sys/menu/delete",
            "/api/sys/menu/sort",
            "/api/sys/config/switch/save"
            ));


    /**
     * 校验接口是不是被阻止运行
     * @param pjp
     * @throws Throwable
     */
    public void checkForbidden(JoinPoint pjp) throws Throwable {

        // 请求地址
        String uri = request.getRequestURI();

        if(this.isForbidden(uri)){
            throw new ServiceException("当前为演示模式，不允许进行此操作！");
        }
    }


    /**
     * 校验被保护的数据不允许操作
     * @param pjp
     * @return
     * @throws Throwable
     */
    public Object protect(ProceedingJoinPoint pjp) throws Throwable {


        log.info("++++++++++检测数据是否受保护");

        // 获得注解
        DataProtect pro = ((MethodSignature)pjp.getSignature()).getMethod().getAnnotation(DataProtect.class);

        // 获得注解表名
        TableName table = (TableName)pro.clazz().getAnnotation(TableName.class);

        // 表名
        String tableName = table.value();

        // 请求参数
        Object [] args = pjp.getArgs();

        // 删除保护
        if(pro.delete()){
            this.checkDelete(pro.currUsr(), tableName, args);
        }

        if(pro.update()){
            this.checkUpdate(pro.currUsr(), tableName, args);
        }

        return pjp.proceed();
    }


    /**
     * 保护数据不被删除
     * @param currUser
     * @param tableName
     * @param args
     */
    private void checkDelete(boolean currUser, String tableName, Object [] args){

        List<String> dataIds = new ArrayList<>();

        if(currUser){
            dataIds.add(protectService.currentUserId());
        }

        for (Object object : args) {
            // 删除方法获取ID列表
            if(object instanceof BaseIdsReqDTO){
                BaseIdsReqDTO reqDTO = (BaseIdsReqDTO)object;
                dataIds.addAll(reqDTO.getIds());
            }
        }

        if(!CollectionUtils.isEmpty(dataIds)){
            // 进行校验
            int count = protectService.countSystemData(tableName, dataIds);
            if(count > 0){
                throw new ServiceException("您不能删除系统数据或演示数据！");
            }
        }
    }


    /**
     * 保护数据不被删除
     * @param currUser
     * @param tableName
     * @param args
     */
    private void checkUpdate(Boolean currUser, String tableName, Object [] args){

        List<String> dataIds = new ArrayList<>();

        if(currUser){
            dataIds.add(protectService.currentUserId());
        }

        for (Object object : args) {
            String id = this.extractId(object);
            if(!StringUtils.isEmpty(id)){
                dataIds.add(id);
            }
        }

        if(!CollectionUtils.isEmpty(dataIds)){
            // 进行校验
            int count = protectService.countSystemData(tableName, dataIds);
            if(count > 0){
                throw new ServiceException("系统数据，不允许修改！");
            }
        }
    }


    /**
     * 获得实体的ID
     * @param object
     * @return
     */
    private String extractId(Object object){

        // 提取全部字段
        Class clazz = object.getClass();
        List<Field> fields = InjectUtils.extractAllFields(clazz);

        for(Field field: fields){
            String name = field.getName();
            if(DATA_ID.equals(name)){
                // 私有属性必须设置访问权限
                field.setAccessible(true);
                try {
                    String id = (String) field.get(object);
                    return id;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }


    /**
     * 是否被禁止访问
     * @param uri
     * @return
     */
    private boolean isForbidden(String uri){

        for(String item: FORBIDDEN_API){
            if(uri.endsWith(item)){
                return true;
            }
        }

        return false;
    }

}
